home *** CD-ROM | disk | FTP | other *** search
/ Mac100% 1998 November / MAC100-1998-11.ISO.7z / MAC100-1998-11.ISO / スクリーンセーバーファクトリー / DarkSide of the Mac 5.0.2 / SampleFaders / Morpion.c < prev    next >
Text File  |  1994-08-26  |  10KB  |  335 lines

  1. /*
  2.     Morpion (French) == 5 In A Row (English)
  3.     The first one who puts 5 markers in a row (horizontal, vertical or diagonal) wins. The computer plays against itself.
  4.     
  5.     This small fader is intended as a code example. It is based on FaderShell, written by Tom Dowdy.
  6.     This code is hereby placed into the public domain. Use it as a template to write your faders!
  7.         
  8.     You can use global variables, they are stored as an offset from A4 using THINK's SetupA4/RestoreA4 procedures.
  9.     
  10.     This fader also demonstrates the use of callback routines by playing sound. Note that in order to play sound, you must
  11.     request a sound channel from DarkSide by creating a 'Chnl' 0 resource. 
  12. */
  13.  
  14. #include <Memory.h>
  15. #include <Windows.h>
  16. #include <Dialogs.h>
  17. #include <Errors.h>
  18. #include <ToolUtils.h>
  19.  
  20. #include "Fader.h"                                // include DarkSide's interface
  21.  
  22. #ifdef THINK_C
  23.     #define QUICKDRAWBLACK    (qd.black)
  24.     #define QUICKDRAWWHITE    (qd.white)
  25. #else
  26.     #define QUICKDRAWBLACK    (&qd.black)
  27.     #define QUICKDRAWWHITE    (&qd.white)
  28. #endif
  29.  
  30. // Constants. These are specific to Morpion, you can throw them away if you are writing a new fader.
  31.  
  32. #define    border        2
  33.  
  34. #define    empty        0
  35. #define    player1        1
  36. #define    player2        2
  37.  
  38. // Global variables, referenced using A4. Also Morpion-specific.
  39. #ifdef powerc
  40.    QDGlobals    qd;
  41. #endif
  42.  
  43. short        xloc, yloc;                        // starting location of the grid
  44. short         xsize, ysize;                        // Width and height of grid
  45. short        xc, yc;                            // Pixel coordinates of topleft corner
  46. Handle        grid, points, possible;    
  47. short        value[5][5];
  48. short        turn;                                // Tells who's up
  49. Boolean        draw, winner;                        // End-of-game flags
  50. short        unit;                                // Size of squares
  51. long        nextDraw;                        // TickCount of the next time to draw
  52.  
  53. // grid is considered as a two dimensional array 0..xsize-1, 0..ysize-1 of chars
  54. // points as an array 0..xsize-1, 0..ysize-1 of shorts
  55. // possible as an array 0..xsize*ysize-1 of points
  56.  
  57. #define    Grid(x, y)        ((char*) (*grid)) [xsize * y + x]
  58. #define    Points(x, y)    ((short*) (*points)) [xsize * y + x]
  59. #define    Possible(x)    ((Point*) (*possible)) [x]
  60.  
  61. void StartGame (void)
  62. {
  63.     long        ix;
  64.     char         *p;
  65.  
  66.     turn = player1;                                                // white plays first
  67.     draw = winner = false;                                        
  68.     for (p = *grid, ix = xsize*ysize; ix; p++, ix--) *p = empty;            // clear the grid
  69. }
  70.  
  71. // Called when fader is starting up, before window has been created.
  72. // We initialize our global variables here.
  73.  
  74. OSErr    PreflightFader(MachineInfoPtr machineInfo, long *minTicks, long *maxTicks)
  75. {    
  76.     Rect        bounds;
  77.     long        gridsize;
  78.     short    a, b;
  79.     
  80.     *minTicks = 1;                                    // Tell DarkSide how often we want to be called.
  81.     *maxTicks = 15;
  82.     
  83.     #ifdef powerc
  84.         BlockMove(machineInfo->applicationQD, &qd, sizeof(qd));
  85.     #endif
  86.     
  87.     // The rest is specific to Morpion.
  88.     
  89.     unit = machineInfo->faderSettings->theShorts[1];                    // read the settings
  90.     bounds = machineInfo->theScreens[0].bounds;                        // compute the size of our grid
  91.     xloc = bounds.left;
  92.     yloc = bounds.top;
  93.     xsize = (bounds.right - bounds.left - 6 * unit) / unit;
  94.     ysize = (bounds.bottom - bounds.top - 6 * unit) / unit;
  95.     xc = bounds.left + (bounds.right - bounds.left - unit * xsize) / 2;
  96.     yc = bounds.top + (bounds.bottom - bounds.top - unit * ysize) / 2;
  97.     
  98.     if (!(grid = BestNewHandle (gridsize = xsize * ysize)))                // allocate memory for the grid
  99.         return memFullErr;
  100.     if (!(possible = BestNewHandle (4*gridsize))) {                        // allocate memory for the 'possible' array
  101.         DisposHandle(grid);
  102.         return memFullErr;
  103.     }
  104.     if (!(points = BestNewHandle (2*gridsize)))    {                    // allocate memory for the 'points' array
  105.         DisposHandle(possible);
  106.         DisposHandle(grid);
  107.         return memFullErr;
  108.     }
  109.     
  110.     for (a = 0; a < 5; a++)                                        // initialize the 'value' array
  111.         for (b = 0; b < 5; b++)
  112.             value [a][b] = 0;
  113.     value [0][0] = 10; value [0][1] = 20; value [0][2] = 80; value [0][3] = 300; value [0][4] = 2000;
  114.     value [1][0] = 20; value [2][0] = 70; value [3][0] = 400; value [4][0] = 4000;
  115.     
  116.     StartGame();
  117.  
  118.     return noErr;
  119. }
  120.  
  121. // Called when fader is starting up, after window has been created. We usually erase the screens here.
  122. // We then draw the grid.
  123.  
  124. OSErr    InitializeFader(MachineInfoPtr machineInfo)
  125. {
  126.     short        screenIndex;
  127.     short        ix;
  128.  
  129.     // store the local coordinates of our windows
  130.     xloc = machineInfo->theScreens[0].bounds.left;
  131.     yloc = machineInfo->theScreens[0].bounds.top;
  132.  
  133.     PenPat(QUICKDRAWBLACK);                                            // erase the screens.
  134.     for (screenIndex = 0; screenIndex < machineInfo->numScreens; screenIndex++) 
  135.         PaintRect(&machineInfo->theScreens[screenIndex].bounds);
  136.         
  137.     PenPat(QUICKDRAWWHITE);                                            // draw the grid.
  138.     SetOrigin(-xloc, -yloc);
  139.     for (ix = 0; ix <= xsize; ix++) {
  140.         MoveTo (xc + ix * unit, yc);
  141.         Line (0, unit * ysize);
  142.     }
  143.     for (ix = 0; ix <= ysize; ix++) {
  144.         MoveTo (xc, yc + ix * unit);
  145.         Line (unit * xsize, 0);
  146.     }
  147.     SetOrigin(0, 0);
  148.     
  149.     nextDraw = 0;
  150.     
  151.     return noErr;
  152. }
  153.  
  154. // The DoRow and Play routines are Morpion's IA. They are not part of the fader's interface with DarkSide.
  155.  
  156. void DoRow (short bx, short by, short dx, short dy)
  157. {
  158.     short     i;
  159.     short     x, y;
  160.     short     nfriend, nenemy;
  161.     char    g;
  162.     
  163.     for (nfriend = nenemy = 0, x = bx, y = by, i = 5; i; x += dx, y += dy, i--) {
  164.         if ((g = Grid(x, y)) == turn)                                // count how many enemy/friendly markers
  165.             nfriend++;                                        // are present in those five squares
  166.         else if (g != empty)
  167.             nenemy++;
  168.     }
  169.     
  170.     for (x = bx, y = by, i = 5; i; x += dx, y += dy, i--)                    // increment the strategic value of the free squares
  171.         if (Grid(x, y) == empty)                                    // depending on nenemy and nfriendly
  172.             Points(x, y) += value [nfriend][nenemy];
  173. }
  174.  
  175. Point Play (void)
  176. {
  177.     short    ix, iy, npos, max, n, p;
  178.     
  179.     for (ix = 0; ix < xsize; ix++)                                    // Only allow empty spots to be played
  180.         for (iy = 0; iy < ysize; iy++)
  181.             Points(ix, iy) = Grid(ix, iy) == empty ? 0 : -1;
  182.                 
  183.     for (ix = 0; ix < xsize-4; ix++)                                    // Walk the whole grid, giving more points to each
  184.         for (iy = 0; iy < ysize; iy++)                                // interesting spot
  185.             DoRow (ix, iy, 1, 0);
  186.     for (ix = 0; ix < xsize; ix++)
  187.         for (iy = 0; iy < ysize-4; iy++)
  188.             DoRow (ix, iy, 0, 1);
  189.     for (ix = 0; ix < xsize-4; ix++)
  190.         for (iy = 0; iy < ysize-4; iy++)
  191.             DoRow (ix, iy, 1, 1);
  192.     for (ix = 0; ix < xsize-4; ix++)
  193.         for (iy = 4; iy < ysize; iy++)
  194.             DoRow (ix, iy, 1, -1);
  195.  
  196.     for (npos = 0, max = -1, ix = 0; ix < xsize; ix++)                    // Look for the most interesting spot
  197.         for (iy = 0; iy < ysize; iy++) {
  198.             p = Points(ix, iy);
  199.             if (p > max) {                                        // If this square is more interesting than all those
  200.                 max = p; npos = 1;                                // previously visited, forget about them
  201.                 Possible (0).h = ix;
  202.                 Possible (0).v = iy;
  203.             }
  204.             else if (p == max) {                                    // If it is equally interesting than the best previously
  205.                 npos++;                                        // visited squares, add it to the array of possible
  206.                 Possible (npos-1).h = ix;                            // moves.
  207.                 Possible (npos-1).v = iy;
  208.             }
  209.         }
  210.     
  211.     if (max == 0)                                                // Nothing interesting : this is a draw
  212.         draw = true;
  213.     else if (npos) {                                                
  214.         n = Random();                                            // Randomly choose a square from the list
  215.         if (n < 0) n = -n;                                        // of best possible squares
  216.         n = n % npos;
  217.  
  218.         if (max >= value [4][0])                                    // This means we just won
  219.             winner = true;
  220.  
  221.         return Possible (n);                                        // Pick up a solution randomly from the set of
  222.     }                                                        // the best solutions
  223.     else
  224.         draw = true;                                            // The grid is full
  225. }
  226.  
  227. // Called regularly. This is the heart of the fader. Here we do whatever we please...
  228.  
  229. OSErr    IdleFader(MachineInfoPtr machineInfo)
  230. {
  231.     Point            location;
  232.     Rect            r;
  233.     long            finalTicks;
  234.     short        a;
  235.     
  236.     // if it isn't time to draw yet, we return -- we do this rather than delay to avoid
  237.     // locking up the machine with a function as mundane as a screensaver
  238.     if (TickCount() < nextDraw)
  239.         return(noErr);
  240.  
  241.     
  242.     location = Play();                                            // find out where we want to play
  243.  
  244.     if (!draw) {                                                // If the player actually played something
  245.         SetOrigin(-xloc, -yloc);
  246.         r.left = xc + border + unit * location.h;
  247.         r.top = yc + border + unit * location.v;
  248.         r.right = r.left + unit - 2 * border + 1;
  249.         r.bottom = r.top + unit - 2 * border + 1;
  250.         if (turn == player1)
  251.             EraseOval (&r);                                    // draw the new marker in the proper color
  252.         else {
  253.             PenPat (QUICKDRAWWHITE);
  254.             FrameOval (&r);
  255.             PenPat (QUICKDRAWBLACK);
  256.         }
  257.         
  258.         (void) PlayResourceSnd (machineInfo, 128, true);                // play sound asynchronously
  259.         for (a = 4; a; a--) {                                        // make it blink so that the user sees it
  260.             InvertRect (&r);
  261.             Delay (2, &finalTicks);
  262.         }
  263.         
  264.         SetOrigin(0, 0);
  265.     }
  266.  
  267.     // calculate the next time to draw    
  268.     nextDraw = TickCount() + 60 - machineInfo->faderSettings->theShorts[0];
  269.  
  270.     if (draw || winner) {                                            // if the game's over
  271.         Str255    resultString;
  272.         
  273.         PenPat (QUICKDRAWBLACK);
  274.         TextMode (srcXor);
  275.         r = machineInfo->theScreens[0].bounds;
  276.         MoveTo (r.left+2*unit, r.top + 2*unit);                        // print a message explaining why
  277.         if (draw)
  278.             {
  279.             GetIndString(resultString, 5000, 1);
  280.             }
  281.         else 
  282.             {
  283.             if (turn == player1)
  284.                 GetIndString(resultString, 5000, 2);
  285.             else
  286.                 GetIndString(resultString, 5000, 3);
  287.             }
  288.         DrawString(resultString);
  289.         PenNormal();
  290.         
  291.         Delay (240, &finalTicks);                                    // wait a few seconds
  292.         StartGame();                                            // and start a new game
  293.         InitializeFader(machineInfo);
  294.         return noErr;
  295.     }
  296.     
  297.  
  298.     Grid(location.h, location.v) = turn;                                // update the grid in memory
  299.     turn = (turn == player1) ? player2 : player1;                        // change turn
  300.         
  301.     return noErr;
  302. }
  303.  
  304. // Called when the fade is tearing down
  305.  
  306. OSErr    DisposeFader(MachineInfoPtr machineInfo)
  307. {
  308. #pragma unused (machineInfo)
  309.  
  310.     DisposHandle (grid);                                            // release all the memory we reserved
  311.     DisposHandle (points);
  312.     DisposHandle (possible);
  313.     return noErr;
  314. }
  315.  
  316. // Called when there is an update event for our fade window.
  317.  
  318. OSErr    UpdateFader(MachineInfoPtr machineInfo)
  319. {
  320.     InitializeFader(machineInfo);                                    // erase the screen, draw the grid
  321.     return noErr;
  322. }
  323.  
  324. // Called when there is an events in the settings dialog. itemHit will be the item the user has selected, or 0 when the dialog
  325. // is being set up. 
  326. // itemHit - itemOffset will allow you to determine which item in your dialog list this corresponds to.
  327. // If you don't wish to do any special processing of this event, simply return fnfErr and the standard effect will take place.
  328.  
  329. OSErr    HitFader(MachineInfoPtr machineInfo, DialogPtr dPtr, short itemHit, short itemOffset)
  330. {
  331. #pragma unused (machineInfo, dPtr, itemHit, itemOffset)
  332.  
  333.     return fnfErr;
  334. }
  335.